package com.zwcl.common.core.utils;

import com.zwcl.common.core.constant.Constants;
import com.zwcl.common.core.exception.BaseException;
import com.zwcl.common.core.exception.UtilsException;
import com.zwcl.common.core.utils.text.StrFormatter;
import org.apache.commons.lang3.StringUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Description 字符工具类
 * 这个类中不应该加一些集合的操作
 * @Author xyp
 * @Date 2019/7/19 15:21
 */
public final class StringUtilsEx extends StringUtils {

    /**
     * TimeEn
     */
    private static final String DEFAULT_DATE_FIELD_NAME_EN = "TimeEn";

    /**
     * TimeCn
     */
    private static final String DEFAULT_DATE_FIELD_NAME_CN = "TimeCn";

    /**
     * En
     */
    private static final String DEFAULT_DATE_EN = "En";

    /**
     * Cn
     */
    private static final String DEFAULT_DATE_CN = "Cn";

    /**
     * ASC
     */
    private static final String DEFAULT_EMPTY_ASC_UPPER = " ASC"; // 前面必须有空格

    /**
     * DESC
     */
    private static final String DEFAULT_EMPTY_DESC_UPPER = " DESC"; // 前面必须有空格

    /**
     * asc
     */
    private static final String DEFAULT_EMPTY_ASC_LOWER = " asc"; // 前面必须有空格

    /**
     * desc
     */
    private static final String DEFAULT_EMPTY_DESC_LOWER = " desc"; // 前面必须有空格

    /**
     * 逗号，（英文状态）
     */
    public static final String DEFAULT_COMMA = ",";

    /**
     * *
     */
    public static final String SENSITIVE_VALUE_STAR = "*";

    /**
     * null 字符串
     */
    public static final String EMPTY_NULL = "null";

    /**
     * 正斜杠 /
     */
    public static final String POSITIVE_SPRIT = "/";

    /** 空字符串 */
    private static final String NULLSTR = "";

    /** 下划线 */
    private static final char SEPARATOR = '_';


    /**
     * 私有化构造器
     */
    private StringUtilsEx() {

    }

    /**
     * @param cs the CharSequence to check, may be null
     * @return {@code true} if the CharSequence is empty or null
     * @since 3.0 Changed signature from isEmpty(String) to isEmpty(CharSequence)
     */
    public static boolean isEmpty(final CharSequence cs) {
        return cs == null || cs.length() == 0 || EMPTY_NULL.equalsIgnoreCase(cs.toString());
    }

    public static boolean isNotEmpty(final CharSequence cs) {

        return !isEmpty(cs) && !EMPTY_NULL.equalsIgnoreCase(cs.toString());
    }

    /**
     * * 判断一个Collection是否为空， 包含List，Set，Queue
     *
     * @param coll 要判断的Collection
     * @return true：为空 false：非空
     */
    public static boolean isEmpty(Collection<?> coll)
    {
        return isNull(coll) || coll.isEmpty();
    }

    /**
     * * 判断一个Collection是否非空，包含List，Set，Queue
     *
     * @param coll 要判断的Collection
     * @return true：非空 false：空
     */
    public static boolean isNotEmpty(Collection<?> coll)
    {
        return !isEmpty(coll);
    }

    /**
     * * 判断一个对象数组是否为空
     *
     * @param objects 要判断的对象数组
     ** @return true：为空 false：非空
     */
    public static boolean isEmpty(Object[] objects)
    {
        return isNull(objects) || (objects.length == 0);
    }

    /**
     * * 判断一个对象数组是否非空
     *
     * @param objects 要判断的对象数组
     * @return true：非空 false：空
     */
    public static boolean isNotEmpty(Object[] objects)
    {
        return !isEmpty(objects);
    }

    /**
     * * 判断一个Map是否为空
     *
     * @param map 要判断的Map
     * @return true：为空 false：非空
     */
    public static boolean isEmpty(Map<?, ?> map)
    {
        return isNull(map) || map.isEmpty();
    }

    /**
     * * 判断一个Map是否为空
     *
     * @param map 要判断的Map
     * @return true：非空 false：空
     */
    public static boolean isNotEmpty(Map<?, ?> map)
    {
        return !isEmpty(map);
    }

    /**
     * * 判断一个字符串是否为空串
     *
     * @param str String
     * @return true：为空 false：非空
     */
    public static boolean isEmpty(String str)
    {
        return isNull(str) || NULLSTR.equals(str.trim());
    }

    /**
     * * 判断一个字符串是否为非空串
     *
     * @param str String
     * @return true：非空串 false：空串
     */
    public static boolean isNotEmpty(String str)
    {
        return !isEmpty(str);
    }

    /**
     * * 判断一个对象是否为空
     *
     * @param object Object
     * @return true：为空 false：非空
     */
    public static boolean isNull(Object object)
    {
        return object == null;
    }

    /**
     * * 判断一个对象是否非空
     *
     * @param object Object
     * @return true：非空 false：空
     */
    public static boolean isNotNull(Object object)
    {
        return !isNull(object);
    }

    /**
     * * 判断一个对象是否是数组类型（Java基本型别的数组）
     *
     * @param object 对象
     * @return true：是数组 false：不是数组
     */
    public static boolean isArray(Object object)
    {
        return isNotNull(object) && object.getClass().isArray();
    }

    /**
     * 去空格
     */
    public static String trim(String str)
    {
        return (str == null ? "" : str.trim());
    }


    /**
     * @param cs the CharSequence to check, may be null
     * @return {@code true} if the CharSequence is null, empty or whitespace
     * @since 3.0 Changed signature from isBlank(String) to isBlank(CharSequence)
     */
    @Deprecated
    public static boolean isBlank(final CharSequence cs) {
        int strLen;
        if (cs == null || (strLen = cs.length()) == 0) {
            return true;
        }
        // ad by Hero ## Begin
        if (EMPTY_NULL.equalsIgnoreCase(cs.toString().trim())) {
            return true;
        }
        // add by Hero ## End
        for (int i = 0; i < strLen; i++) {
            if (Character.isWhitespace(cs.charAt(i)) == false) {
                return false;
            }
        }
        return true;
    }

    /**
     * 首字母大写
     *
     * @param str 字符串
     * @return 返回首字母大写字符串
     */
    @Deprecated
    public static String captureName(String str) {
        return upperCaseForFirst(str);
    }

    /**
     * 首字母大写
     *
     * @param str 字符串
     * @return 返回首字母大写字符串
     */
    public static String upperCaseForFirst(String str) {
        if (isBlank(str)) {
            return null;
        }
        str = str.trim();
        // 首先判断是否为大写
        if (Character.isUpperCase(str.charAt(0))) {
            return str;
        }
        char[] cs = str.toCharArray();
        cs[0] -= 32; // 大写
        return String.valueOf(cs);
    }

    /**
     * 首字母小写
     *
     * @param str 字符串
     * @return 返回首字母小写字符串
     */
    public static String lowerCaseForFirst(String str) {
        if (isBlank(str)) {
            return null;
        }
        str = str.trim();
        // 首先判断是否为小写
        if (Character.isLowerCase(str.charAt(0))) {
            return str;
        }
        char[] cs = str.toCharArray();
        cs[0] += 32;  // 小写
        return String.valueOf(cs);
    }

    /**
     * 获取字符串中所有大写字母，例如：creaeByMebmerIdx ，返回：BMI
     *
     * @param str 字符串
     * @return 返回所有的大写字母
     */
    public static String getAllUpperCase(String str) {
        if (StringUtils.isBlank(str)) {
            return null;
        }
        str = str.trim();
        StringBuffer buffer = new StringBuffer();
        // 转为char数组
        char[] ch = str.toCharArray();
        // 得到大写字母
        for (int i = 0; i < ch.length; i++) {
            if (ch[i] >= 'A' && ch[i] <= 'Z') {
                buffer.append(ch[i]);
            }
        }
        // 倒序
//        buffer = buffer.reverse();
        return buffer.toString();
    }

    /**
     * 根据排序条件，得到数据库表的排序列名，例如: createByMemberIdx ，返回：create_by_member_idx，
     * <p>支持：createByMemberIdx asc,updateByByMemberIdx desc,lastLoginTime</p>
     * <p>转换成：create_by_member_idx asc,update_by_by_member_idx desc,last_login_time</p>
     * <p>注意：排序关键词 desc、asc不区分大小写，最终都统一转换成小写</p>
     * <p>同时支持：createTimeEnYyyyMMddHHmmssSSS 转换成：create_time</p>
     *
     * @param orderBy 排序条件，例如：createByMemberIdx DESC
     * @return 根据POJO的字段名，得到数据库表的列名
     */
    public static String getCorrectOrderByForDB(String orderBy) {
        if (StringUtils.isBlank(orderBy)) {
            return null;
        }
        orderBy = orderBy.trim();

        // 如果有包括 ASC、DESC（大写的），都替换成小写的 asc、desc
        if (orderBy.contains(DEFAULT_EMPTY_ASC_UPPER)) {
            orderBy = orderBy.replace(DEFAULT_EMPTY_ASC_UPPER, DEFAULT_EMPTY_ASC_LOWER);
        } else if (orderBy.contains(DEFAULT_EMPTY_DESC_UPPER)) {
            orderBy = orderBy.replace(DEFAULT_EMPTY_DESC_UPPER, DEFAULT_EMPTY_DESC_LOWER);
        }

        // 如果是日期格式
        // 简单实现，不支持多个条件
        String[] data = null;
        if (orderBy.contains(DEFAULT_DATE_FIELD_NAME_CN)) {
            orderBy = orderBy.split(DEFAULT_DATE_CN)[0];
        } else if (orderBy.contains(DEFAULT_DATE_FIELD_NAME_EN)) {
            orderBy = orderBy.split(DEFAULT_DATE_EN)[0];
        }

        // TODO: 需要完善，兼容多个条件的日期格式：
        // createTimeEnYyyyMMddHHmmssSSS asc,createTimeCnYyyyMMddHHmmssSSS desc,lastLoginTime
//        fieldName = getFileNames(fieldName);


        StringBuffer buffer = new StringBuffer();
        // 转为char数组
        char[] ch = orderBy.toCharArray();
        String separator = "-"; // 不可以是 .
        // 得到大写字母
        for (int i = 0; i < ch.length; i++) {
            if (ch[i] >= 'A' && ch[i] <= 'Z') {
                buffer.append(ch[i]);
                if (i < ch.length - 2) {
                    buffer.append(separator);
                }
            }
        }
        // 如果是大写字母，就在前面加 "_" ， 例如: createByMemberIdx ，返回：create_by_member_idx
        // 同时都转换成小写，因为数据库中列名，都是小写，在 MySQL数据库军规中！！！

        if (buffer.length() <= 0) {
            return orderBy;
        }
        String[] strs = buffer.toString().split(separator);

        if (null == strs || strs.length <= 0) {
            return orderBy;
        }

        String line = "_";
        for (String str : strs) {
            orderBy = orderBy.replace(str, line + str.toLowerCase());
        }

        return orderBy;
    }

    /**
     * 获取字符串的长度，如果有中文，则每个中文字符计为2位
     *
     * @param value 指定的字符串
     * @return 字符串的长度
     */
    public static int length(String value) {
        return Optional.ofNullable(value).map(s -> s.length()).orElse(0);
    }

    /**
     * 解决 GET请求参数中文乱码问题
     *
     * @param parameter GET请求参数
     * @return 返回正常（非乱码）参数
     * @throws UtilsException 异常
     */
    public static String getNormalGetRequestParameter(String parameter) throws UtilsException {
        if (StringUtilsEx.isBlank(parameter)) {
            return null;
        }
        parameter = parameter.trim();
        try {
            // 解决 GET请求参数，中文乱码问题
            return new String(parameter.getBytes(Constants.CHARSET_ISO8859_1), Constants.CHARSET_UTF_8);
        } catch (Exception ex) {
            throw new UtilsException(ex);
        }
    }

    /**
     * 固定长度的值
     *
     * @param length 长度
     * @param value  字符串，没有去掉左右两边的空格
     * @return 返回固定长度的值
     * @throws UtilsException 异常
     */
    public static String fixedLengthValue(int length, String value) throws UtilsException {
        if (null == value || length <= 0) {
            return value;
        }
//        value = value.trim();

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            sb.append(value);
        }
        return sb.toString();
    }

    /**
     * 将手机号的4-7位替换成 *
     *
     * @param phone 手机号码
     * @return 替换后的手机号码
     */
    public static String replacePhone(final String phone) {
        if (isBlank(phone) || phone.trim().length() != 11) {
            return phone;
        }
        return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
    }

    /**
     * 将身份证号码中间几位替换成*
     *
     * @param identityCard 身份证号码
     * @return 替换后的身份证号码
     */
    public static String replaceIdentityCard(final String identityCard) {
        if (isBlank(identityCard)) {
            return identityCard;
        } else {
            int length = identityCard.length() - 14;
            return length <= 0 ? identityCard : identityCard.replaceAll("(\\d{6})\\d{8}(\\d{" + length + "})", "$1********$2");
        }
    }

    /**
     * 替换特殊字符
     *
     * @param str 字符串
     * @return 替换后的身份证号码
     */
    public static String replaceSpecialCharacters(String str) {
        if (isBlank(str)) {
            return str;
        } else {
            return str.replaceAll("\\+", SPACE).replaceAll("\\*", SPACE)
                    .replaceAll("&", SPACE).replaceAll("\\【", SPACE).replaceAll("\\】", SPACE)
                    .replaceAll("[?]", SPACE).replaceAll("\\'", SPACE).replaceAll("/", SPACE);
        }
    }

    /**
     * 替换不可见字符
     *
     * @param str 字符串
     * @return 替换结果
     */
    public static String replaceBlank(String str) {
        String dest = "";
        if (str != null) {
            Pattern p = Pattern.compile("\\s*|\t|\r|\n");
            Matcher m = p.matcher(str);
            dest = m.replaceAll("");
        }
        return dest;
    }

    /**
     * 获取windows 地址
     *
     * @return
     * @throws Exception
     */
    private static String getIPOnWindows() throws Exception {
        String ip = "";
        BufferedReader bufferedReader = null;
        Process process = null;
        try {
            process = Runtime.getRuntime().exec("ipconfig");
            bufferedReader = new BufferedReader(new InputStreamReader(process
                    .getInputStream()));
            String line = null;
            int index = -1;
            boolean isLocal = false;
            while ((line = bufferedReader.readLine()) != null) {
                //找到本地连接信息
                if (line.toLowerCase().indexOf("ethernet adapter 本地连接:") >= 0 ||
                        line.indexOf("以太网适配器 本地连接:") >= 0 ||
                        line.indexOf("以太网适配器 以太网:") >= 0) {
                    isLocal = true;
                }

                //找到IP信息
                if (line.toLowerCase().indexOf("ipv4") >= 0 ||
                        line.toLowerCase().indexOf("ip address") >= 0) {
                    if (isLocal) {
                        index = line.indexOf(":");
                        ip = line.substring(index + 1).trim();
                        break;
                    }
                }
            }
            //没有取到本地连接中的IP信息，则通过Java API来获取
            if (!StringUtilsEx.isEmpty(ip)) {
                InetAddress inet = InetAddress.getLocalHost();
                ip = inet.getHostAddress();
            }
        } catch (IOException e) {
            throw e;
        } finally {
            try {
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
            } catch (IOException e1) {
            }
            bufferedReader = null;
            process = null;
        }

        return ip;
    }

    /**
     * Linux下获取本机IP地址
     *
     * @return IP地址
     */
    private static String getIPOnLinux() throws Exception {
        String ip = "";
        try {
            Enumeration<?> e1 = (Enumeration<?>) NetworkInterface
                    .getNetworkInterfaces();
            while (e1.hasMoreElements()) {
                NetworkInterface ni = (NetworkInterface) e1.nextElement();
                if (!ni.getName().equals("eth0")) {
                    continue;
                } else {
                    Enumeration<?> e2 = ni.getInetAddresses();
                    while (e2.hasMoreElements()) {
                        InetAddress ia = (InetAddress) e2.nextElement();
                        if (ia instanceof Inet6Address)
                            continue;
                        ip = ia.getHostAddress();
                    }
                    break;
                }
            }
        } catch (Exception e) {
        }

        return ip;
    }

    public static String getIPAddress() {
        String ip = null;
        try {
            String os = System.getProperty("os.name");
            ip = os.startsWith("Windows") ? getIPOnWindows() : getIPOnLinux();
        } catch (Exception e) {
        }

        return ip;
    }

    public static String toString(Object obj) {
        return obj != null ? obj.toString().trim() : "";
    }

    /**
     * 从字符串中提取数字
     *
     * @param s
     * @return
     */
    public static String numberFilter(String s) {
        String regEx = "[^0-9]";
        Pattern p = Pattern.compile(regEx);
        Matcher m = p.matcher(s);
        return m.replaceAll("").trim();
    }

    /**
     * 格式化文本, {} 表示占位符<br>
     * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
     * 如果想输出 {} 使用 \\转义 { 即可，如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
     * 例：<br>
     * 通常使用：format("this is {} for {}", "a", "b") -> this is a for b<br>
     * 转义{}： format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
     * 转义\： format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
     *
     * @param template 文本模板，被替换的部分用 {} 表示
     * @param params 参数值
     * @return 格式化后的文本
     */
    public static String format(String template, Object... params)
    {
        if (isEmpty(params) || isEmpty(template))
        {
            return template;
        }
        return StrFormatter.format(template, params);
    }

    /**
     * 下划线转驼峰命名
     */
    public static String toUnderScoreCase(String str)
    {
        if (str == null)
        {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        // 前置字符是否大写
        boolean preCharIsUpperCase = true;
        // 当前字符是否大写
        boolean curreCharIsUpperCase = true;
        // 下一字符是否大写
        boolean nexteCharIsUpperCase = true;
        for (int i = 0; i < str.length(); i++)
        {
            char c = str.charAt(i);
            if (i > 0)
            {
                preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
            }
            else
            {
                preCharIsUpperCase = false;
            }

            curreCharIsUpperCase = Character.isUpperCase(c);

            if (i < (str.length() - 1))
            {
                nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
            }

            if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase)
            {
                sb.append(SEPARATOR);
            }
            else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase)
            {
                sb.append(SEPARATOR);
            }
            sb.append(Character.toLowerCase(c));
        }

        return sb.toString();
    }

    /**
     * 是否包含字符串
     *
     * @param str 验证字符串
     * @param strs 字符串组
     * @return 包含返回true
     */
    public static boolean inStringIgnoreCase(String str, String... strs)
    {
        if (str != null && strs != null)
        {
            for (String s : strs)
            {
                if (str.equalsIgnoreCase(trim(s)))
                {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空，则返回空字符串。 例如：HELLO_WORLD->HelloWorld
     *
     * @param name 转换前的下划线大写方式命名的字符串
     * @return 转换后的驼峰式命名的字符串
     */
    public static String convertToCamelCase(String name)
    {
        StringBuilder result = new StringBuilder();
        // 快速检查
        if (name == null || name.isEmpty())
        {
            // 没必要转换
            return "";
        }
        else if (!name.contains("_"))
        {
            // 不含下划线，仅将首字母大写
            return name.substring(0, 1).toUpperCase() + name.substring(1);
        }
        // 用下划线将原始字符串分割
        String[] camels = name.split("_");
        for (String camel : camels)
        {
            // 跳过原始字符串中开头、结尾的下换线或双重下划线
            if (camel.isEmpty())
            {
                continue;
            }
            // 首字母大写
            result.append(camel.substring(0, 1).toUpperCase());
            result.append(camel.substring(1).toLowerCase());
        }
        return result.toString();
    }

    /**
     * 驼峰式命名法 例如：user_name->userName
     */
    public static String toCamelCase(String s)
    {
        if (s == null)
        {
            return null;
        }
        s = s.toLowerCase();
        StringBuilder sb = new StringBuilder(s.length());
        boolean upperCase = false;
        for (int i = 0; i < s.length(); i++)
        {
            char c = s.charAt(i);

            if (c == SEPARATOR)
            {
                upperCase = true;
            }
            else if (upperCase)
            {
                sb.append(Character.toUpperCase(c));
                upperCase = false;
            }
            else
            {
                sb.append(c);
            }
        }
        return sb.toString();
    }


    /**
     * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
     *
     * @param str 指定字符串
     * @param strs 需要检查的字符串数组
     * @return 是否匹配
     */
    public static boolean matches(String str, List<String> strs)
    {
        if (isEmpty(str) || isEmpty(strs))
        {
            return false;
        }
        for (String testStr : strs)
        {
            if (matches(str, testStr))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 查找指定字符串是否匹配指定字符串数组中的任意一个字符串
     *
     * @param str 指定字符串
     * @param strs 需要检查的字符串数组
     * @return 是否匹配
     */
    public static boolean matches(String str, String... strs)
    {
        if (isEmpty(str) || isEmpty(strs))
        {
            return false;
        }
        for (String testStr : strs)
        {
            if (matches(str, testStr))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 查找指定字符串是否匹配
     *
     * @param str 指定字符串
     * @param pattern 需要检查的字符串
     * @return 是否匹配
     */
    public static boolean matches(String str, String pattern)
    {
        if (isEmpty(pattern) || isEmpty(str))
        {
            return false;
        }

        pattern = pattern.replaceAll("\\s*", ""); // 替换空格
        int beginOffset = 0; // pattern截取开始位置
        int formerStarOffset = -1; // 前星号的偏移位置
        int latterStarOffset = -1; // 后星号的偏移位置

        String remainingURI = str;
        String prefixPattern = "";
        String suffixPattern = "";

        boolean result = false;
        do
        {
            formerStarOffset = indexOf(pattern, SENSITIVE_VALUE_STAR, beginOffset);
            prefixPattern = substring(pattern, beginOffset, formerStarOffset > -1 ? formerStarOffset : pattern.length());

            // 匹配前缀Pattern
            result = remainingURI.contains(prefixPattern);
            // 已经没有星号，直接返回
            if (formerStarOffset == -1)
            {
                return result;
            }

            // 匹配失败，直接返回
            if (!result)
                return false;

            if (!isEmpty(prefixPattern))
            {
                remainingURI = substringAfter(str, prefixPattern);
            }

            // 匹配后缀Pattern
            latterStarOffset = indexOf(pattern, SENSITIVE_VALUE_STAR, formerStarOffset + 1);
            suffixPattern = substring(pattern, formerStarOffset + 1, latterStarOffset > -1 ? latterStarOffset : pattern.length());

            result = remainingURI.contains(suffixPattern);
            // 匹配失败，直接返回
            if (!result)
                return false;

            if (!isEmpty(suffixPattern))
            {
                remainingURI = substringAfter(str, suffixPattern);
            }

            // 移动指针
            beginOffset = latterStarOffset + 1;

        }
        while (!isEmpty(suffixPattern) && !isEmpty(remainingURI));

        return true;
    }

    @SuppressWarnings("unchecked")
    public static <T> T cast(Object obj)
    {
        return (T) obj;
    }
}
